#include "PS2_Client.h"

/*******************************************************************************
 * Function Name  : InitPS2
 * Description    : 初始化PS2设备
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
PS2_Client::PS2_Client(int clk, int d) {
    pin_clk = clk;
    pin_d = d;

    pinMode(pin_clk, OUTPUT);
    pinMode(pin_d, OUTPUT);

    // 释放时钟线和数据线
    digitalWrite(pin_clk, HIGH);
    digitalWrite(pin_d, HIGH);

    PS2_TX_S = 0; // 发送缓冲开始位置
    PS2_TX_E = 0; // 发送缓冲结束位置

    setDefault();
}

// 发送一位
uint8_t PS2_Client::send_bit(uint8_t bit) {
    digitalWrite(pin_d, bit);
    ets_delay_us(20);
    if (!digitalRead(pin_clk))
        return 0;
    digitalWrite(pin_clk, LOW);
    ets_delay_us(40);
    digitalWrite(pin_clk, HIGH);
    ets_delay_us(20);
    if (!digitalRead(pin_clk))
        return 0;
    return 1;
}

// 读取一位
uint8_t PS2_Client::read_bit(uint8_t *bit) {
    ets_delay_us(20);
    if (!digitalRead(pin_clk))
        return 0;
    digitalWrite(pin_clk, LOW);
    ets_delay_us(40);
    digitalWrite(pin_clk, HIGH);
    ets_delay_us(20);
    if (!digitalRead(pin_clk))
        return 0;
    if (digitalRead(pin_d)) {
        *bit = 0x80;
    } else {
        *bit = 0x00;
    }

    return 1;
}

/*******************************************************************************
 * Function Name  : ps2_sendByte
 * Description    : 发送一个字节
 * Input          : timeout：尝试次数
 * Output         : None
 * Return         : 0： success  1：fail
 *******************************************************************************/
uint8_t PS2_Client::sendByte(uint8_t data) {

    // 检测1
    if (!digitalRead(pin_clk)) {
        // 被主机抢占
        return 1;
    }

    digitalWrite(pin_d, HIGH);
    ets_delay_us(50);

    // 检测2
    if (!digitalRead(pin_clk)) {
        // 被主机抢占
        return 1;
    }

    if (!digitalRead(pin_d)) {
        // 主机在输出
        return 1;
    }

    PS2_TXByte = data; // 储存正在发送的字节，以防主机发送FE命令

    // 现在是clk(1),d(1)
    ets_delay_us(20);
    send_bit(0); // 发送起始位

    uint8_t i = 0;
    uint8_t sendBuf = 0; // 发送的数据
    uint8_t dataChk = 1; // 奇偶校验
    uint8_t dataBit = 0; // 接收/发送的数据位

    // 发送数据
    sendBuf = data;
    for (; i < 8; i++) {
        dataBit = sendBuf & 0x01;
        dataChk ^= dataBit;
        if (!send_bit(dataBit)) {
            digitalWrite(pin_d, HIGH);
            return 1;
        }
        sendBuf >>= 1;
    }

    // 发送校验位
    if (!send_bit(dataChk)) {
        digitalWrite(pin_d, HIGH);
        return 1;
    }

    // 发送停止位
    send_bit(1);
    ets_delay_us(30);

    printf("sent:x%02X\n", data);
    return 0;
}

void PS2_Client::writeBuffer(uint8_t data) {
    PS2_TXBuffer[PS2_TX_E++] = data;
}

/*******************************************************************************
 * Function Name  : ps2_sendData
 * Description    : 将缓存中的数据发送出去
 * Input          :
 * Output         : None
 * Return         : None
 *******************************************************************************/
void PS2_Client::sendData(void) {
    while (PS2_TX_S != PS2_TX_E) {
        if (0 == sendByte(PS2_TXBuffer[PS2_TX_S])) {
            PS2_TX_S++;
        } else {
            return;
        }
    }
}

/*******************************************************************************
 * Function Name  : ps2_readData
 * Description    : 接收数据到缓存
 * Input          :
 * Output         : None
 * Return         : 0: 没有数据
 *                  1: success(读到正确数据)
 *******************************************************************************/
int PS2_Client::readData(uint8_t &buf) {

    // 检测1
    if (!digitalRead(pin_clk)) {
        // 主机拉低中，未到发送阶段，下次在试
        return 0;
    }

    // 检测2
    if (digitalRead(pin_d)) {
        // 主机并未输出
        return 0;
    }

    // 现在是clk(1),d(0), 处在起始位
    uint8_t readBuf = 0; // 接收的数据
    uint8_t dataChk = 0; // 奇偶校验
    uint8_t dataBit = 0; // 接收的数据位

    // 读取8位数据
    uint8_t i = 0;
    for (; i < 8; i++) {
        read_bit(&dataBit);
        readBuf >>= 1;
        readBuf |= dataBit;
        dataChk ^= dataBit;
    }

    // 读取校验位
    read_bit(&dataBit);
    dataChk ^= dataBit;

    // 读取停止位
    read_bit(&dataBit);
    if (!digitalRead(pin_d)) {
        while (!digitalRead(pin_d)) {
            digitalWrite(pin_clk, LOW);
            ets_delay_us(30);
            digitalWrite(pin_clk, HIGH);
            ets_delay_us(30);
        }
        return 0;
    }

    // 输出应答位
    ets_delay_us(20);
    send_bit(0);
    digitalWrite(pin_d, HIGH);

    // 协议里没写的内容
    /*
    ets_delay_us(10);
    ps2_clk = 0;
    ets_delay_us(20);
    ps2_clk = 1;
    */

    ets_delay_us(45);

    // 校验
    if (dataChk) {
        buf = readBuf;
        return 1;
    }

    // 读取到错误数据
    sendByte(0xFE); // 请求重发
    return 0;
}

// 设备复位
void PS2_Client::setDefault() {
}

void PS2_Client::clearTXBuffer() {
    PS2_TX_S = PS2_TX_E;
}

// 处理过则返回true
bool PS2_Client::executeCmd(uint8_t cmd) {

    switch (cmd) {
    case 0xEE:          // 查响应
        sendByte(0xEE); // 回复
        break;
    case 0xFE:                // 重新发送最后一个字节
        sendByte(PS2_TXByte); // 回复
        break;
    default:
        sendByte(0xFA); // 回复
        break;
    }
}

void PS2_Client::execute() {

    // 根据设备状态生成发送数据
    checkChange();

    // 发送数据
    sendData();

    uint8_t cmd;

    // 接收到缓存
    if (!readData(cmd)) {
        return;
    }

    printf("cmd[x%02X]\n", cmd);

    // 执行指令
    executeCmd(cmd);
}